﻿using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System;
using System.Net;
using System.Threading.Tasks;

namespace DotNetty.Wraper
{
    class UdpSocketBuilder : IUdpSocketBuilder
    {
        public UdpSocketBuilder(int port)
        {
            Port = port;
        }
        public int Port { get; }
        UdpSocketEvent _socketEvent { get; } = new UdpSocketEvent();

        public IUdpSocketBuilder OnChannelRegistered(Action<IUdpSocket, Transport.Channels.IChannel> action)
        {
            _socketEvent.OnChannelRegistered = action;

            return this;
        }

        public IUdpSocketBuilder OnChannelUnregistered(Action<IUdpSocket, Transport.Channels.IChannel> action)
        {
            _socketEvent.OnChannelUnregistered = action;

            return this;
        }
        public IUdpSocketBuilder OnClose(Action<IUdpSocket> action)
        {
            _socketEvent.OnClose = action;

            return this;
        }

        public IUdpSocketBuilder OnException(Action<Exception> action)
        {
            _socketEvent.OnException = action;

            return this;
        }

        public IUdpSocketBuilder OnRecieve(Action<IUdpSocket, EndPoint, byte[]> action)
        {
            _socketEvent.OnRecieve = action;

            return this;
        }

        public IUdpSocketBuilder OnSend(Action<IUdpSocket, EndPoint, byte[]> action)
        {
            _socketEvent.OnSend = action;

            return this;
        }

        public IUdpSocketBuilder OnStarted(Action<IUdpSocket> action)
        {
            _socketEvent.OnStarted = action;

            return this;
        }

        public async Task<IUdpSocket> BuildAsync(Func<IUdpSocket, IChannelHandler> addChannelHandler = null, Action<Transport.Channels.IChannelPipeline> OnPipelineAction = null
            , bool useLibuv = false, System.Security.Cryptography.X509Certificates.X509Certificate2 tlsCertificate = null)
        {
            var tcpClient = new UdpSocket(Port, _socketEvent);

            IEventLoopGroup group;
            if (useLibuv)
            {
                group = new Transport.Libuv.EventLoopGroup();
            }
            else
            {
                group = new MultithreadEventLoopGroup();
            }
            var clientChannel = await new Bootstrap()
                .Group(group)
                .Channel<SocketDatagramChannel>()
                .Option(ChannelOption.SoBroadcast, true)
                .Handler(new ActionChannelInitializer<IChannel>(channel =>
                {
                    IChannelPipeline pipeline = channel.Pipeline;
                    //if (tlsCertificate != null)
                    //{
                    //    pipeline.AddLast(Handlers.Tls.TlsHandler.Server(tlsCertificate));
                    //}
                    OnPipelineAction?.Invoke(pipeline);

                    if (addChannelHandler != null)
                        pipeline.AddLast(addChannelHandler(tcpClient));
                    else
                        pipeline.AddLast(new CommonChannelHandler(tcpClient));

                })).BindAsync(Port);

            tcpClient.SetChannel(clientChannel);
            tcpClient.AfterClose = () =>
            {
                group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)).Wait();
            };
            return await Task.FromResult(tcpClient);
        }
    }
}
